/***********************************************************************************
 * 文件名： st7789v.c
 * 作者： 刘言
 * 版本： 1
 * 说明：
 *      ST7789V 驱动。ST7789V采用【命令-参数】模式进行操作，先发送命令，再发送参数。发命
 *  令时RS=0，发参数（数据）时RS=1. 常见的操作逻辑是发送一些设置工作参数的命令进行初始化，
 *  发送设置窗口（设置起始和结束地址）命令来设置操作窗口，发送写入显存的命令，之后开始写入
 *  数据填充缓存，每次写入数据后指针自动加一，当写入到窗口末尾后指针支持自动归位到窗口起始
 *  位置。
 *      初始化函数的内容可能依据不同的LCD要做一些修改，建议参考LCD厂商的例程进行修改。
 *      目前本驱动仅实现了全双工SPI接口模式，该芯片支持半双工SPI和多种并口。
 * 修改记录：
 * 	2022/09/05: 初版。 刘言。
 ***********************************************************************************/
#include "st7735sv.h"
#include "..\lcd.h"
#include "lcd_config.h"
#include "polsys.h"

void Lcd_DispOff()
{
    _Lcd_BkLightOff();
    WriteOneByteCmd(_CMD_DIS_OFF); // display off
}

void Lcd_DispOn()
{
    WriteOneByteCmd(_CMD_DIS_ON); // display on
    // _Lcd_Delayms(120);
    WriteOneByteCmd(_CMD_WRITE_MEM);
    _Lcd_BkLightOn();
}
// __inline void LCD_SetCursor(u16 x,u16 y) 不支持
// {
// 	LCD_WriteRegister(0x0200,y); // 行	  0200
// 	LCD_WriteRegister(0x0201,x); // 列	  0201
// }

void Lcd_SetInvertDisplay(bool state)
{
    if (state)
    {
        WriteOneByteCmd(_CMD_DIS_INVERSION_ON); // 反向显示
    }
    else
    {
        WriteOneByteCmd(_CMD_DIS_INVERSION_OFF); // 正向显示
    }
}

void Lcd_SetWindow(u16 start_x, u16 start_y, u16 end_x, u16 end_y)
{
    // 偏移地址
#if LCD_OFFSET_X != 0
    start_x += LCD_OFFSET_X;
    end_x += LCD_OFFSET_X;
#endif
#if LCD_OFFSET_Y
    start_y += LCD_OFFSET_Y;
    end_y += LCD_OFFSET_Y;
#endif

    WriteOneByteCmd(_CMD_SET_COL_ADDR);
    WriteOneByteData(start_x >> 8);
    WriteOneByteData(start_x);
    WriteOneByteData(end_x >> 8);
    WriteOneByteData(end_x);
    WriteOneByteCmd(_CMD_SET_ROW_ADDR);
    WriteOneByteData(start_y >> 8);
    WriteOneByteData(start_y);
    WriteOneByteData(end_y >> 8);
    WriteOneByteData(end_y);
    WriteOneByteCmd(_CMD_WRITE_MEM);
}

// 直接画一个点。低效率API，不建议使用。你应该使用操作缓存的替代API，然后一次性写入缓存。
// 注意：会改变当前操作窗口设置。
// 返回：true:操作成功，false:总线忙。
bool Lcd_SetPoint(u16 x, u16 y, lcd_color_t point)
{
    if (_Lcd_PortBusy())
        return false;
    Lcd_SetWindow(x, y, x, y);
    _Lcd_PortWrite((u8*)&point, 2);
    while (_Lcd_PortBusy())
        ;
    return true;
}

// [异步]填充数据
// 在当前设置的窗口内依次填充数据
// 返回：true:操作成功，false:总线忙
// 可以通过总线数据发送完毕事件来判断是否操作结束。
bool Lcd_FillData(u8* dat, u32 length)
{
    if (_Lcd_PortBusy())
        return false;
    _Lcd_PortWrite(dat, length);
    return true;
}

// 以下内部函数

// 仅初始化时使用
void WriteOneByteCmd(u8 cmd)
{
    _Lcd_SetRsLevel(0);
    // while(_Lcd_PortBusy());
    _Lcd_PortWrite(&cmd, 1);
    while (_Lcd_PortBusy())
        ;
    _Lcd_SetRsLevel(1);
}

// 仅初始化时使用
void WriteOneByteData(u8 dat)
{
    _Lcd_SetRsLevel(1);
    // while(_Lcd_PortBusy());
    _Lcd_PortWrite(&dat, 1);
    while (_Lcd_PortBusy())
        ;
}
